package com.itbaizhan.shopping_seckill_service.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itbaizhan.shopping_common.exception.BusException;
import com.itbaizhan.shopping_common.pojo.CartGoods;
import com.itbaizhan.shopping_common.pojo.Orders;
import com.itbaizhan.shopping_common.pojo.SeckillGoods;
import com.itbaizhan.shopping_common.result.CodeEnum;
import com.itbaizhan.shopping_common.service.SecKillService;
import com.itbaizhan.shopping_seckill_service.mapper.SecKillGoodsMapper;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author spf
 * @date 2024/1/21
 * @time 16:56
 */

/**
 * 秒杀商品实现类
 */
@Service
@Component
@Transactional
public class SecKillServiceImpl implements SecKillService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private SecKillGoodsMapper secKillGoodsMapper;

    @Scheduled(cron = "0/5 * * * * *")
    public void refreshRedis() {
        System.out.println("从redis中同步数据");

        // 将redis中的库存数据同步到数据库中
        List<SeckillGoods> seckillGoodsListOld = redisTemplate.boundHashOps("seckillGoods").values();
        for (SeckillGoods seckillGoods : seckillGoodsListOld) {
            SeckillGoods sqlSeckillGoods = secKillGoodsMapper.selectById(seckillGoods.getId());
            sqlSeckillGoods.setStockCount(seckillGoods.getStockCount());
            secKillGoodsMapper.updateById(sqlSeckillGoods);
        }

        // 查询数据库中正在秒杀的商品
        QueryWrapper<SeckillGoods> queryWrapper = new QueryWrapper<>();
        Date date = new Date();
        String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
        queryWrapper.le("startTime", now);
        queryWrapper.ge("endTime", now);
        queryWrapper.gt("stockCount", 0);
        List<SeckillGoods> seckillGoodsList = secKillGoodsMapper.selectList(queryWrapper);

        // 删除reids中的商品数据
        redisTemplate.delete("seckillGoods");

        // 保存现在正在秒杀的商品
        for (SeckillGoods seckillGoods : seckillGoodsList) {
            redisTemplate.boundHashOps("seckillGoods").put(seckillGoods.getGoodsId(), seckillGoods);
        }
    }

    @Override
    public Page<SeckillGoods> findPageByRedis(int page, int size) {
        // 获取所有秒杀商品列表
        List seckillGoods = redisTemplate.boundHashOps("seckillGoods").values();

        // 获取当前页数据
        int start = (page - 1) * size;
        int end = start + size > seckillGoods.size() ? seckillGoods.size() : start + size;

        // 获取当前页面结果集
        List seckillGoodsList = seckillGoods.subList(start, end);

        // 封装成页面对象
        Page<SeckillGoods> seckillGoodsPage = new Page<>();
        seckillGoodsPage.setCurrent(page)
                .setSize(size)
                .setTotal(seckillGoods.size())
                .setRecords(seckillGoodsList);
        return seckillGoodsPage;
    }

    @Override
    public SeckillGoods findSecKillGoodsByRedis(Long goodsId) {
        SeckillGoods seckillGoods = (SeckillGoods) redisTemplate.boundHashOps("seckillGoods").get(goodsId);
        return seckillGoods;
    }

    @Override
    public Orders createOrder(Orders orders) {
        orders.setId(IdWorker.getIdStr()); // 设置订单编号
        orders.setStatus(2);
        orders.setCreateTime(new Date());
        orders.setExpire(new Date(new Date().getTime() + 1000 * 60 * 5));

        CartGoods cartGoods = orders.getCartGoods().get(0);
        BigDecimal num = BigDecimal.valueOf(cartGoods.getNum());
        BigDecimal price = cartGoods.getPrice();
        BigDecimal sum = num.multiply(price);
        orders.setPayment(sum);

        // 减少秒杀商品库存
        SeckillGoods seckillGoods = findSecKillGoodsByRedis(cartGoods.getGoodId());
        Integer stockCount = seckillGoods.getStockCount();
        if (stockCount <= 0) {
            throw new BusException(CodeEnum.NO_STOCK_ERROR);
        }
        seckillGoods.setStockCount(stockCount - cartGoods.getNum());
        redisTemplate.boundHashOps("seckillGoods").put(seckillGoods.getGoodsId(), seckillGoods);

        // 保存订单数据
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.opsForValue().set(orders.getId(), orders, 1, TimeUnit.MINUTES);

        /**
         * 给订单创建副本，副本的过期时间长于原订单
         * redis过期后触发过期事件时，redis数据已经过期，此时只能拿到key，拿不到value。
         * 而过期事件需要回退商品库存，必须拿到value即订单详情，才能拿到商品数据，进行回退操作
         * 我们保存一个订单副本，过期时间长于原订单，此时就可以通过副本拿到原订单数据
         */
        redisTemplate.opsForValue().set(orders.getId() + "_copy", orders, 2, TimeUnit.MINUTES);
        return orders;
    }

    @Override
    public Orders findOrder(String id) {
        Orders orders = (Orders)redisTemplate.opsForValue().get(id);
        return orders;
    }

    @Override
    public Orders pay(String orderId) {
        Orders orders = (Orders)redisTemplate.opsForValue().get(orderId);
        if (orders == null){
            throw new BusException(CodeEnum.ORDER_EXPIRED_ERROR);
        }

        orders.setStatus(2);
        orders.setPaymentType(2);
        orders.setPaymentTime(new Date());

        // 删除redis中的数据
        redisTemplate.delete(orderId);
        redisTemplate.delete(orderId + "_copy");

        return orders;
    }
}
